//===--- AstPrinter.h - Class for printing the AST --------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#ifndef POLARPHP_AST_ASTPRINTER_H
#define POLARPHP_AST_ASTPRINTER_H

#include "polarphp/basic/LLVM.h"
#include "polarphp/basic/Uuid.h"
#include "polarphp/ast/Identifier.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/Support/raw_ostream.h"
#include "polarphp/ast/PrintOptions.h"

namespace polar::llparser {
enum class tok;
} // polar

namespace polar {

using polar::llparser::tok;
class SourceLoc;
class Decl;
class DeclContext;
class DynamicSelfType;
class ModuleEntity;
class TypeDecl;
class EnumElementDecl;
class Type;
struct TypeLoc;
class Pattern;
class ExtensionDecl;
class NominalTypeDecl;
class ValueDecl;
enum class AccessorKind;

/// Describes the context in which a name is being printed, which
/// affects the keywords that need to be escaped.
enum class PrintNameContext {
   /// Normal context
      Normal,
   /// Keyword context, where no keywords are escaped.
      Keyword,
   /// Type member context, e.g. properties or enum cases.
      TypeMember,
   /// Generic parameter context, where 'Self' is not escaped.
      GenericParameter,
   /// Class method return type, where 'Self' is not escaped.
      ClassDynamicSelf,
   /// Function parameter context, where keywords other than let/var/inout are
   /// not escaped.
      FunctionParameterExternal,
   FunctionParameterLocal,
   /// Tuple element context, similar to \c FunctionParameterExternal.
      TupleElement,
   /// Attributes, which are escaped as 'Normal', but differentiated for
   /// the purposes of printName* callbacks.
      Attribute,
};

/// Describes the kind of structured entity being printed.
///
/// This includes printables with sub-structure that cannot be completely
/// handled by the printDeclPre/printDeclPost callbacks.
/// E.g.
/// \code
///   func foo(<FunctionParameter>x: Int = 2</FunctionParameter>, ...)
/// \endcode
enum class PrintStructureKind {
   GenericParameter,
   GenericRequirement,
   FunctionParameter,
   FunctionType,
   FunctionReturnType,
   BuiltinAttribute,
   TupleType,
   TupleElement,
   NumberLiteral,
   StringLiteral,
};

/// An abstract class used to print an AST.
class AstPrinter {
   unsigned CurrentIndentation = 0;
   unsigned PendingNewlines = 0;
   TypeOrExtensionDecl SynthesizeTarget;

   void printTextImpl(StringRef Text);

public:
   virtual ~AstPrinter() {}

   virtual void printText(StringRef Text) = 0;

   // MARK: Callback interface.

   /// Called after the printer decides not to print D.
   ///
   /// Callers should use callAvoidPrintDeclPost().
   virtual void avoidPrintDeclPost(const Decl *D) {};
   /// Called before printing of a declaration.
   ///
   /// Callers should use callPrintDeclPre().
   virtual void printDeclPre(const Decl *D, Optional<BracketOptions> Bracket) {}
   /// Called before printing at the point which would be considered the location
   /// of the declaration (normally the name of the declaration).
   ///
   /// Callers should use callPrintDeclLoc().
   virtual void printDeclLoc(const Decl *D) {}
   /// Called after printing the name of the declaration.
   virtual void printDeclNameEndLoc(const Decl *D) {}
   /// Called after printing the name of a declaration, or in the case of
   /// functions its signature.
   virtual void printDeclNameOrSignatureEndLoc(const Decl *D) {}
   /// Called after finishing printing of a declaration.
   ///
   /// Callers should use callPrintDeclPost().
   virtual void printDeclPost(const Decl *D, Optional<BracketOptions> Bracket) {}

   /// Called before printing the result type of the declaration. Printer can
   /// replace \p TL to customize the input.
   virtual void printDeclResultTypePre(ValueDecl *VD, TypeLoc &TL) {}

   /// Called before printing a type.
   virtual void printTypePre(const TypeLoc &TL) {}
   /// Called after printing a type.
   virtual void printTypePost(const TypeLoc &TL) {}

   /// Called when printing the referenced name of a type declaration, possibly
   /// from deep inside another type.
   ///
   /// \param T the original \c Type being referenced. May be null.
   /// \param RefTo the \c TypeDecl this is considered a reference to.
   /// \param Name the name to be printed.
   /// \param NameContext the \c PrintNameContext which this type is being
   ///                    printed in, used to determine how to escape type names.
   virtual void printTypeRef(
      Type T, const TypeDecl *RefTo, Identifier Name,
      PrintNameContext NameContext = PrintNameContext::Normal);

   /// Called when printing the referenced name of a module.
   virtual void printModuleRef(ModuleEntity Mod, Identifier Name);

   /// Called before printing a synthesized extension.
   virtual void printSynthesizedExtensionPre(const ExtensionDecl *ED,
                                             TypeOrExtensionDecl NTD,
                                             Optional<BracketOptions> Bracket) {}

   /// Called after printing a synthesized extension.
   virtual void printSynthesizedExtensionPost(const ExtensionDecl *ED,
                                              TypeOrExtensionDecl TargetDecl,
                                              Optional<BracketOptions> Bracket) {
   }

   /// Called before printing a structured entity.
   ///
   /// Callers should use callPrintStructurePre().
   virtual void printStructurePre(PrintStructureKind Kind,
                                  const Decl *D = nullptr) {}
   /// Called after printing a structured entity.
   virtual void printStructurePost(PrintStructureKind Kind,
                                   const Decl *D = nullptr) {}

   /// Called before printing a name in the given context.
   virtual void printNamePre(PrintNameContext Context) {}
   /// Called after printing a name in the given context.
   virtual void printNamePost(PrintNameContext Context) {}

   // Helper functions.

   void printSeparator(bool &first, StringRef separator) {
      if (first) {
         first = false;
      } else {
         printTextImpl(separator);
      }
   }

   AstPrinter &operator<<(StringRef Text) {
      printTextImpl(Text);
      return *this;
   }

   AstPrinter &operator<<(unsigned long long N);
   AstPrinter &operator<<(UUID UU);

   AstPrinter &operator<<(DeclName name);

   // Special case for 'char', but not arbitrary things that convert to 'char'.
   template <typename T>
   typename std::enable_if<std::is_same<T, char>::value, AstPrinter &>::type
   operator<<(T c) {
      return *this << StringRef(&c, 1);
   }

   void printKeyword(StringRef name, PrintOptions Opts, StringRef Suffix = "") {
      if (Opts.SkipUnderscoredKeywords && name.startswith("_"))
         return;
      callPrintNamePre(PrintNameContext::Keyword);
      *this << name;
      printNamePost(PrintNameContext::Keyword);
      *this << Suffix;
   }

   void printAttrName(StringRef name, bool needAt = false) {
      callPrintNamePre(PrintNameContext::Attribute);
      if (needAt)
         *this << "@";
      *this << name;
      printNamePost(PrintNameContext::Attribute);
   }

   AstPrinter &printSimpleAttr(StringRef name, bool needAt = false) {
      callPrintStructurePre(PrintStructureKind::BuiltinAttribute);
      printAttrName(name, needAt);
      printStructurePost(PrintStructureKind::BuiltinAttribute);
      return *this;
   }

   void printEscapedStringLiteral(StringRef str);

   void printName(Identifier Name,
                  PrintNameContext Context = PrintNameContext::Normal);

   void setIndent(unsigned NumSpaces) {
      CurrentIndentation = NumSpaces;
   }

   void setSynthesizedTarget(TypeOrExtensionDecl Target) {
      assert((!SynthesizeTarget || !Target || Target == SynthesizeTarget) &&
             "unexpected change of setSynthesizedTarget");
      // FIXME: this can overwrite the original target with nullptr.
      SynthesizeTarget = Target;
   }

   void printNewline() {
      PendingNewlines++;
   }

   void forceNewlines() {
      if (PendingNewlines > 0) {
         llvm::SmallString<16> Str;
         for (unsigned i = 0; i != PendingNewlines; ++i)
            Str += '\n';
         PendingNewlines = 0;
         printText(Str);
         printIndent();
      }
   }

   virtual void printIndent();

   // MARK: Callback interface wrappers that perform AstPrinter bookkeeping.

   /// Make a callback to printDeclPre(), performing any necessary bookeeping.
   void callPrintDeclPre(const Decl *D, Optional<BracketOptions> Bracket);

   /// Make a callback to printDeclPost(), performing any necessary bookeeping.
   void callPrintDeclPost(const Decl *D, Optional<BracketOptions> Bracket) {
      printDeclPost(D, Bracket);
   }

   /// Make a callback to avoidPrintDeclPost(), performing any necessary
   /// bookkeeping.
   void callAvoidPrintDeclPost(const Decl *D) {
      avoidPrintDeclPost(D);
   }

   /// Make a callback to printDeclLoc(), performing any necessary bookeeping.
   void callPrintDeclLoc(const Decl *D) {
      forceNewlines();
      printDeclLoc(D);
   }

   /// Make a callback to printNamePre(), performing any necessary bookeeping.
   void callPrintNamePre(PrintNameContext Context) {
      forceNewlines();
      printNamePre(Context);
   }

   /// Make a callback to printStructurePre(), performing any necessary
   /// bookkeeping.
   void callPrintStructurePre(PrintStructureKind Kind, const Decl *D = nullptr) {
      forceNewlines();
      printStructurePre(Kind, D);
   }

   /// To sanitize a malformed utf8 string to a well-formed one.
   static std::string sanitizeUtf8(StringRef Text);

private:
   virtual void anchor();
};

/// An AST printer for a raw_ostream.
class StreamPrinter : public AstPrinter {
protected:
   raw_ostream &OS;

public:
   explicit StreamPrinter(raw_ostream &OS) : OS(OS) {}

   void printText(StringRef Text) override;
};

/// AST stream printer that adds extra indentation to each line.
class ExtraIndentStreamPrinter : public StreamPrinter {
   StringRef ExtraIndent;

public:
   ExtraIndentStreamPrinter(raw_ostream &out, StringRef extraIndent)
      : StreamPrinter(out), ExtraIndent(extraIndent) { }

   virtual void printIndent() {
      printText(ExtraIndent);
      StreamPrinter::printIndent();
   }
};

void printContext(raw_ostream &os, DeclContext *dc);

bool printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
                          Type AdopterTy, SourceLoc TypeLoc, raw_ostream &OS);

/// Print a keyword or punctuator directly by its kind.
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, tok keyword);

/// Get the length of a keyword or punctuator by its kind.
uint8_t getKeywordLen(tok keyword);

/// Get <#code#>;
StringRef getCodePlaceholder();

/// Given an array of enum element decls, print them as case statements with
/// placeholders as contents.
void printEnumElementsAsCases(
   llvm::DenseSet<EnumElementDecl *> &UnhandledElements,
   llvm::raw_ostream &OS);

void getInheritedForPrinting(const Decl *decl, const PrintOptions &options,
                             llvm::SmallVectorImpl<TypeLoc> &Results);

StringRef getAccessorKindString(AccessorKind value);
} // namespace polar

#endif // POLARPHP_AST_ASTPRINTER_H
